cURLコマンドで「クエリ文字列が在るURL」を扱うときはURLを囲もう、という話
お試しでAPIをサクッと叩くとき、cURLコマンドをよく使います。とてもお手軽で便利なのですが、雰囲気で使っていたのでハマりました。
ハマったこと
とあるAPIに対して、クエリ文字列付きで下記のようにアクセスしましたが、「APIキーが無効だよ」のメッセージが返ってきました。
curl https://xxxxx.com/todo?id=aaa&apikey=bbb
そのときは「APIキーを作ったばかりなのでしばらく待ってみよう。ドキュメントにも数時間待ってねと書いてあるし。」と考えましたが、24時間以上が経過しても「APIキーが無効だよ」が返ってくるのです。 わけが分かりませんでした。
何が起きてたか
シェルの制御に使う文字(&
)がコマンドの引数にある場合、文字列の解釈がそこで終わっていました。
たとえば、上記の例を分かりやすくすると次のようになります。
curl https://xxxxx.com/todo?id=aaa&echo bbb
&
はバックグランド実行指定のため、「curl https://xxxxx.com/todo?id=aaa
をバックグランドで実行して、次にecho bbb
を実行する動作」になっています。
解決方法
curl
コマンドのURLをダブルクオートで囲めばOKでした。
curl "https://xxxxx.com/todo?id=aaa&apikey=bbb"
または&
をエスケープシーケンスしてあげるとOKでした。
curl https://xxxxx.com/todo?id=aaa\&apikey=bbb
APIを作って再現してみた
受け取ったクエリ文字列をそのまま返すAPIを作って試してみました。
AWS SAMテンプレート
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: QueryStringApiSample Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.7 Timeout: 10 Events: HelloWorld: Type: Api Properties: Path: /hello Method: get Outputs: HelloWorldApi: Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello"
Lambdaコード
import json def lambda_handler(event, context): return { 'statusCode': 200, 'body': json.dumps({ 'message': event['queryStringParameters'], }), }
ビルド&デプロイ
sam build sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-sam-test-bucket sam deploy \ --template-file packaged.yaml \ --stack-name Query-String-Sample-Stack \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
APIエンドポイント取得
aws cloudformation describe-stacks \ --stack-name Query-String-Sample-Stack \ --query 'Stacks[].Outputs'
NGの場合
&
以降のクエリ文字列が無視されています。
$ curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello?id=aaa&apikey=bbb {"message": {"id": "aaa"}}
OKの場合
&
以降のクエリ文字列もバッチリ扱えました。
$ curl "https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello?foo=aaa&bar=bbb" {"message": {"apikey": "bbb", "id": "aaa"}}
エスケープシーケンスした場合も同様にOKでした。
curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello?id=aaa\&apikey=bbb {"message": {"apikey": "bbb", "id": "aaa"}}
さいごに
「もしかして……?」と気づくのに時間が掛かってしまいました。どなたかの参考になれば幸いです。